using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Data.SqlClient;
using RikMigrations.Providers;

namespace RikMigrations
{
	public class Table
	{
		internal DbProvider provider;

		List<Command> commands = new List<Command>();

		public Table(DbProvider provider, string name, bool createNew)
		{
			this.provider = provider;
			this.name = name;
			if (createNew)
				commands.Add(provider.GetCreateTableCommand(this));
			isCreating = createNew;
		}

		#region Properties
		private string name;
		public string Name
		{
			get { return name; }
		}

		private bool isCreating;
		public bool IsCreating
		{
			get { return isCreating; }
		}

		List<Column> columns = new List<Column>();
		public List<Column> Columns
		{
			get { return columns; }
		}

		List<Constraint> constraints = new List<Constraint>();
		public List<Constraint> Constraints
		{
			get { return constraints; }
		}
		#endregion

        public Column AddColumn<T>(string name)
        {
            return AddColumn(name, typeof(T));
        }
        
        public Column AddColumn<T>(string name, int length)
        {
            return AddColumn(name, typeof(T), length);
        }

		public Column AddColumn(string name, Type type)
		{
			Column retVal = new Column(this, name, type);
			if (isCreating)
				columns.Add(retVal);
			else
				commands.Add(provider.GetAddColumnCommand(retVal));
			return retVal;
		}

		public Column AddColumn(string name, Type type, int length)
		{
			Column retVal = new Column(this, name, type, length);
			if (isCreating)
				columns.Add(retVal);
			else
				commands.Add(provider.GetAddColumnCommand(retVal));
			return retVal;
		}

		public Column AlterColumn(string name)
		{
			if (isCreating)
				throw new InvalidOperationException("Unable to alter column when creating a new table");
			return new Column(this, name);
		}

		public Constraint AddConstraint(string name, ConstraintType constraintType, params string[] columns)
		{
			Constraint retVal = new Constraint(name, constraintType, this, columns);
			if (isCreating)
				constraints.Add(retVal);
			else
				commands.Add(new Constraint.AddConstraintCommand(retVal));
			return retVal;
		}

		public ForeignKey AddForeignKey(string name, string column, string foreignTable, string foreignColumn)
		{
			ForeignKey retVal = new ForeignKey(name, this, column, foreignTable, foreignColumn);
			if (isCreating)
				throw new NotImplementedException("Use the 'References' method on the column to add foreign keys during table creation.");
			else
				commands.Add(provider.GetAddForeignKeyCommand(retVal));
			return retVal;
		}

		public void DropColumn(string name)
		{
			commands.Add(provider.GetDropColumnCommand(this, name));
		}

		public void DropConstraint(string name)
		{
			commands.Add(new Constraint.DropConstraintCommand(this, name));
		}

		[Obsolete("DbProvider is no longer required for Save method. Use parameterless overload")]
		public void Save(DbProvider db)
		{
			Save();
		}

		public void Save()
		{
			foreach (Command cmd in commands)
				cmd.Execute(provider);
			commands.Clear();
			constraints.Clear();
			isCreating = false;
		}

		public List<ForeignKey> ForeignKeys
		{
			get { return provider.FindForeignKeys(this); }
		}

		public ForeignKey FindForeignKey(string column, string foreignTable, string foreignColumn)
		{
			foreach (ForeignKey key in ForeignKeys)
				if (key.Column == column && key.ForeignTable == foreignTable && key.ForeignColumn == foreignColumn)
					return key;
			return null;
		}

	    public void Insert(object objectToInsert)
	    {
            commands.Add(provider.GetInsertCommand(Name, objectToInsert, FindPrimaryKey().IsAutoGenerated));
	    }

	    private Column FindPrimaryKey()
	    {
	        return Columns.Find(delegate(Column c) { return c.IsPrimaryKey; });
	    }
	}
}